package main.model.persistence;

import java.awt.Point;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.FileAlreadyExistsException;
import java.util.HashMap;
import java.util.List;

import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;

import main.model.Attribute;
import main.model.Cardinality;
import main.model.Diagram;
import main.model.Entity;
import main.model.Hierarchy;
import main.model.LayoutComponents;
import main.model.Relation;

import com.sun.xml.internal.txw2.output.IndentingXMLStreamWriter;

public class DiagramXmlWriter {

	static private String DIAGRAM = "diagrama";
	static private String ENTITIES = "entidades";
	static private String RELATIONS = "relaciones";
	static private String ENTITY = "entidad";
	static private String MASTER = "maestra";
	static private String COMPOSITION = "composicion";
	static private String RELATION = "relacion";
	static private String HIERARCHY = "jerarquia";
	static private String HIERARCHIES = "jerarquias";
	static private String NAME = "nombre";
	static private String TYPE = "tipo";
	static private String ID = "id";
	static private String ATTRIBUTE = "atributo";
	static private String ATTRIBUTES = "atributos";
	static private String CARDINALITIES = "cardinalidades";
	static private String CARDINALITY = "cardinalidad";
	static private String MAX = "maxima";
	static private String MIN = "minima";
	static private String TRUE = "true";
	static private String FALSE = "false";
	static private String PARENT_DIAGRAM = "diagramaPadre";
	static private String RELATED_ENTITIES = "entidadesRelacionadas";
	static private String PARENT_ENTITY = "entidadPadre";
	static private String CHILD_ENTITIES = "entidadesHijas";
	static private String POSITION = "posicion";
	static private String X = "x";
	static private String Y = "y";



	XMLOutputFactory factory = XMLOutputFactory.newInstance();

	public void writeTo(String path,String pathPositions, Diagram diagram, HashMap<Diagram, LayoutComponents> layouts, boolean overwrite) {

		writeDiagrams(path,diagram,overwrite);
		writePositions(pathPositions,layouts,diagram, overwrite);

	}

	private void writeDiagrams(String path, Diagram diagram,
			boolean overwrite) {
		try {
			if (!overwrite && (new File(path)).exists()) {
				throw new FileAlreadyExistsException(path);
			}


			XMLStreamWriter writer = new IndentingXMLStreamWriter(factory.createXMLStreamWriter(new FileWriter(path)));

			writer.writeStartDocument();
			
			writer.writeStartElement(DIAGRAM);
			
			writer.writeStartElement(NAME);
			writer.writeCharacters(diagram.getName());
			writer.writeEndElement();
			
			writer.writeStartElement(ENTITIES);
			List<Entity> enitites = diagram.getEntities();
			for (Entity entity : enitites) {
				writeEntity(entity, writer);
			}
			
			writer.writeEndElement();
			
			if ( !diagram.getRelations().isEmpty() ){
				writer.writeStartElement(RELATIONS);
				List<Relation> relations = diagram.getRelations();
				for (Relation relation : relations) {
					writeRelation(relation, writer);
				}
				
				writer.writeEndElement();
			}
			if ( !diagram.getHierarchies().isEmpty() ){
				writer.writeStartElement(HIERARCHIES);
				List<Hierarchy> hierarchies = diagram.getHierarchies();
				for (Hierarchy hierarchy : hierarchies) {
					writeHierarchy(hierarchy, writer);
				}
				
				writer.writeEndElement();
			}	
			
			writer.writeEndElement();
			// Diagram
			writer.writeEndDocument();

			writer.flush();
			writer.close();

		} catch (XMLStreamException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	private void writePositions(String pathPositions,
			HashMap<Diagram, LayoutComponents> layouts, Diagram d,  boolean overwrite) {
		try {
			if (!overwrite && (new File(pathPositions)).exists()) {
				throw new FileAlreadyExistsException(pathPositions);
			}

			XMLStreamWriter writer = new IndentingXMLStreamWriter(factory.createXMLStreamWriter(new FileWriter(pathPositions)));

			writer.writeStartDocument();
			writer.writeStartElement(DIAGRAM);

			HashMap<String,Point> entities = new HashMap<String, Point>();
			HashMap<String,Point> relations = new HashMap<String, Point>();
			HashMap<String,Point> attributes = new HashMap<String, Point>();

			LayoutComponents layoutComponents = layouts.get(d);
			writer.writeStartElement(NAME);
			writer.writeCharacters(d.getName());
			writer.writeEndElement();
			
			for (Entity e : d.getEntities()) {
				entities.put(e.getName(), layoutComponents.getPositionComponent(e));
				for (Attribute att : e.getAttributes()) {
					attributes.put(att.getName(), layoutComponents.getPositionComponent(att));
				}
				for (Attribute att : e.getIdAttributes()) {
					attributes.put(att.getName(), layoutComponents.getPositionComponent(att));
				}
			}
			for (Relation r : d.getRelations()) {
				relations.put(r.getName(), layoutComponents.getPositionComponent(r));
				for (Attribute att : r.getAttributes()) {
					attributes.put(att.getName(), layoutComponents.getPositionComponent(att));
				}
				for (Attribute att : r.getIdAttributes()) {
					attributes.put(att.getName(), layoutComponents.getPositionComponent(att));
				}
			}

			this.writeEntitiesPositions(entities,writer);
			this.writeAttributesPositions(attributes, writer);
			this.writeRelationsPositions(relations, writer);

			writer.writeEndElement();


			writer.flush();
			writer.close();

		} catch (XMLStreamException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}


	private void writeEntitiesPositions(HashMap<String, Point> entities,
			XMLStreamWriter writer) throws XMLStreamException {

		if ( !entities.isEmpty() ){
			writer.writeStartElement(ENTITIES);
			for (String key : entities.keySet()) {
				this.writeComponentPosition(ENTITY, key ,entities.get(key), writer);
			}
			writer.writeEndElement();
		}
	}

	private void writeAttributesPositions(HashMap<String, Point> attributes,
			XMLStreamWriter writer) throws XMLStreamException {
		if ( !attributes.isEmpty() ){
			writer.writeStartElement(ATTRIBUTES);
			for (String key : attributes.keySet()) {
				this.writeComponentPosition(ATTRIBUTE, key ,attributes.get(key), writer);
			}
			writer.writeEndElement();
		}
	}

	private void writeRelationsPositions(HashMap<String, Point> relations,
			XMLStreamWriter writer) throws XMLStreamException {
		if ( !relations.isEmpty() ){
			writer.writeStartElement(RELATIONS);
			for (String key : relations.keySet()) {
				this.writeComponentPosition(RELATION, key ,relations.get(key), writer);
			}
			writer.writeEndElement();
		}	
	}

	private void writeComponentPosition(String componentType, String name, Point point,
			XMLStreamWriter writer) throws XMLStreamException {

		writer.writeStartElement(componentType);
		writer.writeStartElement(NAME);
		writer.writeCharacters(name);
		writer.writeEndElement();
		writer.writeStartElement(POSITION);
		writer.writeStartElement(X);
		writer.writeCharacters(Integer.toString(point.x));
		writer.writeEndElement();
		writer.writeStartElement(Y);
		writer.writeCharacters(Integer.toString(point.y));
		writer.writeEndElement();
		writer.writeEndElement();
		writer.writeEndElement();

	}


	private void writeHierarchy(Hierarchy h, XMLStreamWriter writer) 
			throws XMLStreamException {

		
		writer.writeStartElement(HIERARCHY);
		
		writer.writeStartElement(NAME);
		writer.writeCharacters(h.getName());
		writer.writeEndElement();
		
		writer.writeStartElement(TYPE);
		writer.writeCharacters(h.getType());
		writer.writeEndElement();	
		
		writer.writeStartElement(PARENT_ENTITY);
		writer.writeCharacters(h.getParent().getName());
		writer.writeEndElement();
		
		writer.writeStartElement(CHILD_ENTITIES);
		List<Entity> entities = h.getEntities();
		for (Entity entity : entities) {
			writeEntity(entity, writer);
		}
		writer.writeEndElement();
		
		writer.writeEndElement();
	}

	private void writeRelation(Relation r, XMLStreamWriter writer) 
			throws XMLStreamException {
		
		writer.writeStartElement(RELATION);
		String esComposicion = TRUE;
		if (!r.isComposition()){
			esComposicion = FALSE;
		}
		writer.writeAttribute(COMPOSITION, esComposicion);
		
		writer.writeStartElement(NAME);
		writer.writeCharacters(r.getName());
		writer.writeEndElement();
		writeRelatedEntities(r,writer);
		List<Attribute> idAtts = r.getIdAttributes();
		writeAttributes(idAtts, writer, TRUE);
		List<Attribute> attributes = r.getAttributes();
		writeAttributes(attributes, writer, FALSE);
		writer.writeEndElement();
	}

	private void writeEntity(Entity e, XMLStreamWriter writer)
			throws XMLStreamException {
		
		writer.writeStartElement(ENTITY);
		String maestra = "true";
		if (!e.isMasterEntity()){
			maestra = "false";
		}
		writer.writeAttribute(MASTER, maestra);
		writer.writeAttribute(PARENT_DIAGRAM, e.getDiagramParent());
		
		writer.writeStartElement(NAME);
		writer.writeCharacters(e.getName());
		writer.writeEndElement();
		List<Attribute> idAtts = e.getIdAttributes();
		if ( !idAtts.isEmpty() ){
			writeAttributes(idAtts, writer, TRUE);
		}
		List<Attribute> attributes = e.getAttributes();
		if ( !attributes.isEmpty() ){
			writeAttributes(attributes, writer, FALSE);
		}
		writer.writeEndElement();
	}

	private void writeCardinality(Cardinality c, XMLStreamWriter writer)
			throws XMLStreamException {
		
		writer.writeStartElement(MAX);
		writer.writeCharacters(c.getMax());
		writer.writeEndElement();
		
		writer.writeStartElement(MIN);
		writer.writeCharacters(c.getMin());
		writer.writeEndElement();
		
	}

	private void writeAttributeCardinalities(List<Attribute> attributes, XMLStreamWriter writer) throws XMLStreamException {
		
		writer.writeStartElement(CARDINALITIES);
		for (Attribute attribute : attributes) {
			Cardinality c =  attribute.getCardinality();
			if(c != null){
				
				writer.writeStartElement(CARDINALITY);
				
				writer.writeStartElement(ATTRIBUTE);
				writer.writeCharacters(attribute.getName());
				writer.writeEndElement();
				writeCardinality(c, writer);
				writer.writeEndElement();
			}
		}
		
		writer.writeEndElement();
		
	}

	private void writeAttributes(List<Attribute> atts, XMLStreamWriter writer, String isId) throws XMLStreamException {
		if(!atts.isEmpty()){
			
			writer.writeStartElement(ATTRIBUTES);
			writer.writeAttribute(ID, isId);
			for (Attribute attribute : atts) {
				writeAttribute(attribute, writer);
			}
			
			writer.writeEndElement();
			writeAttributeCardinalities(atts,writer);
		}else{
			
		}
	}

	private void writeAttribute(Attribute a, XMLStreamWriter writer) throws XMLStreamException {
		
		writer.writeStartElement(ATTRIBUTE);
		String tipo = a.getType();
		writer.writeAttribute(TYPE, tipo);
		
		writer.writeStartElement(NAME);
		writer.writeCharacters(a.getName());
		writer.writeEndElement();
		List<Attribute> atts =  a.getAttributes();
		if(!atts.isEmpty()){
			writeAttributes(atts,writer, FALSE);
		}else{
			
		}
		writer.writeEndElement();
	}




	private void writeRelatedEntities(Relation r, XMLStreamWriter writer) throws XMLStreamException {
		
		writer.writeStartElement(RELATED_ENTITIES);
		List<Entity> entities = r.getEntities();
		for (Entity entity : entities) {
			writeRelatedEntity(entity, r.getCardinalityForEntity(entity),writer);
		}
		
		writer.writeEndElement();
	}

	private void writeRelatedEntity(Entity e,Cardinality c,  XMLStreamWriter writer) throws XMLStreamException {
		
		writer.writeStartElement(ENTITY);
		
		writer.writeStartElement(NAME);
		writer.writeCharacters(e.getName());
		writer.writeEndElement();
		
		writer.writeStartElement(CARDINALITY);
		writeCardinality(c, writer);
		writer.writeEndElement();			
		
		writer.writeEndElement();
	}

}
